package com.carter.springframework.core.annotation;

import com.carter.springframework.beans.exception.BeanException;
import com.carter.springframework.util.ConcurrentReferenceHashMap;
import com.sun.istack.internal.Nullable;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * AnnotationTypeMappings用于表示某一个注解类上全部元注解，对应的还有一个AnnotationTypeMapping，它表示一个具体的元注解对象
 * AnnotationTypeMappings与MergedAnnotations的设计思路一样，它表示一组AnnotationTypeMapping对象的聚合状态同时用于提供AnnotationTypeMapping的创建和搜索等功能
 */
final class AnnotationTypeMappings {

    private static final Map<AnnotationFilter, Cache> standardRepeatablesCache = new ConcurrentReferenceHashMap<>();

    private static final Map<AnnotationFilter, Cache> noRepeatablesCache = new ConcurrentReferenceHashMap<>();

    private final RepeatableContainers repeatableContainers;

    private final AnnotationFilter filter;

    private final List<AnnotationTypeMapping> mappings;


    private AnnotationTypeMappings(RepeatableContainers repeatableContainers, AnnotationFilter filter, Class<? extends Annotation> annotationType) {
        //可重复注解的容器
        this.repeatableContainers = repeatableContainers;
        //过滤
        this.filter = filter;
        //映射关系
        this.mappings = new ArrayList<>();
        //解析当前类以及其元注解的层次结构中涉及到的全部映射关系
        addAllMappings(annotationType);
        //映射关系解析完后对别名的一些校验
        this.mappings.forEach(AnnotationTypeMapping::afterAllMappingsSet);
    }

    private void addAllMappings(Class<? extends Annotation> annotationType) {
        //广度优先遍历注解和元注解
        Deque<AnnotationTypeMapping> queue = new ArrayDeque<>();
        addIfPossible(queue,null,annotationType,null);
        while (!queue.isEmpty()){
            AnnotationTypeMapping mapping = queue.removeFirst();
            mappings.add(mapping);
            //继续解析下一层
            addMetaAnnotationsToQueue(queue, mapping);
        }
    }

    private void addMetaAnnotationsToQueue(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source) {
        //获取当前注解上直接声明的元注解
        Annotation[] metaAnnotations  = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);
        for (Annotation metaAnnotation : metaAnnotations) {
            //如果已经解析过了则跳过，避免循环引用
            if (!isMappable(source,metaAnnotation)){
                continue;
            }
            //a.若当前正在解析的注解是容器注解，则将内部的可重复注解取出解析
            Annotation[] repeatedAnnotations = this.repeatableContainers.findRepeatedAnnotations(metaAnnotation);
            if (repeatedAnnotations!=null){
                for (Annotation repeatedAnnotation : repeatedAnnotations) {
                    //1.2.1判断是否已经完成映射
                    if (!isMappable(source,repeatedAnnotation)){
                        continue;
                    }
                    addIfPossible(queue,source,repeatedAnnotation);
                }
            }else {
                //b.若当前正在解析的注解不是容器注解，则将直接解析
                addIfPossible(queue,source,metaAnnotation);
            }
        }
    }

    // 1.2.1 判断是否已经完成映射
    private boolean isMappable(AnnotationTypeMapping source, @Nullable Annotation metaAnnotation) {
        return (metaAnnotation != null && !this.filter.matches(metaAnnotation) &&
                !AnnotationFilter.PLAIN.matches(source.getAnnotationType()) &&
                !isAlreadyMapped(source, metaAnnotation));
    }
    private boolean isAlreadyMapped(AnnotationTypeMapping source, Annotation metaAnnotation) {
        Class<? extends Annotation> annotationType = metaAnnotation.annotationType();
        // 递归映射表，确定这个注解类型是否在映射表的树结构中存在
        // 这个做法相当于在循环引用中去重
        AnnotationTypeMapping mapping = source;
        while (mapping != null) {
            if (mapping.getAnnotationType() == annotationType) {
                return true;
            }
            mapping = mapping.getSource();
        }
        return false;
    }

    private void addIfPossible(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source, Annotation ann) {
        addIfPossible(queue, source, ann.annotationType(), ann);
    }

    private void addIfPossible(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source, Class<? extends Annotation> annotationType, Annotation ann) {
        //将数据源、元注解类型和元注解实例封装为一个AnnotationTypeMapping，作为下一次处理的数据源
        try {
            queue.addLast(new AnnotationTypeMapping(source,annotationType,ann));
        }catch (Exception e){
            throw new BeanException("Failed to introspect meta-annotation " + annotationType.getName());
        }
    }

    public static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType) {
        return null;
    }

    /**
     * 根据一个注解类型创建AnnotationTypeMappings对象实例
     * @param annotationType
     * @param repeatableContainers
     * @param annotationFilter
     * @return
     */
    static AnnotationTypeMappings forAnnotationType(Class<? extends Annotation> annotationType, RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter){
        //针对可重复注解的容器缓存
        if (repeatableContainers == RepeatableContainers.standardRepeatables()) {
            return standardRepeatablesCache.computeIfAbsent(annotationFilter,
                    key -> new Cache(repeatableContainers, key)).get(annotationType);
        }
        //针对不可重复注解的容器缓存
        if (repeatableContainers == RepeatableContainers.none()) {
            return noRepeatablesCache.computeIfAbsent(annotationFilter,
                    key -> new Cache(repeatableContainers, key)).get(annotationType);
        }
        //创建一个AnnotationTypeMappings实例
        return new AnnotationTypeMappings(repeatableContainers, annotationFilter,
                annotationType);
    }

    public AnnotationTypeMapping get(int i) {
        return null;
    }

    public int size() {
        return mappings.size();
    }

    private static class Cache {

        private final RepeatableContainers repeatableContainers;

        private final AnnotationFilter filter;

        private final Map<Class<? extends Annotation>, AnnotationTypeMappings> mappings;

        /**
         * Create a cache instance with the specified filter.
         * @param filter the annotation filter
         */
        Cache(RepeatableContainers repeatableContainers, AnnotationFilter filter) {
            this.repeatableContainers = repeatableContainers;
            this.filter = filter;
            this.mappings = new ConcurrentReferenceHashMap<>();
        }

        /**
         * Get or create {@link AnnotationTypeMappings} for the specified annotation type.
         * @param annotationType the annotation type
         * @return a new or existing {@link AnnotationTypeMappings} instance
         */
        AnnotationTypeMappings get(Class<? extends Annotation> annotationType) {
            return this.mappings.computeIfAbsent(annotationType, this::createMappings);
        }

        AnnotationTypeMappings createMappings(Class<? extends Annotation> annotationType) {
            return new AnnotationTypeMappings(this.repeatableContainers, this.filter, annotationType);
        }
    }
}
